﻿using Sustainalytics.LongRunningTaskManager;
using Sustainalytics.LongRunningTaskManager.Interface;
using Sustainalytics.OAuth.Services.Common.Client;
using Sustainalytics.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;

using Sustainalytics.FileManager;
using System.Net.Http.Formatting;
using Newtonsoft.Json;

using System.Configuration;
using System.Net.Http.Headers;
using System.IO;

using Sustainalytics.DataLayer.ConnectionStrings;
using Sustainalytics.Entities.ConnectionStrings;
using Sustainalytics.ESGRatings.BusinessLogic.Exceptions;
using Sustainalytics.ESGRatings.Service;
using Sustainalytics.ESGRatings.Tasks.Exceptions;

namespace Sustainalytics.ESGRatings.Tasks
{

    public class WeightMatrixUploadTask : ILongRunningTask
    {
        public LongRunningTaskTypeEnum Type
        {
            get { return LongRunningTaskTypeEnum.WeightMatrixUpload; }
        }

        public object Start(LongRunningTaskContext context)
        {
            // get upload data from parameter
            WeightMatrixUploadDto param = GetParameter(context.ParamJson);

            // get current LRTM host config (or any other means to obtain config)
            var taskManagerHostConfig = LongRunningTaskManagerHostConfiguration.Current;

            // create file manager
            var fileManager = new DiskFileManager(taskManagerHostConfig.DiskFileManagerBaseDirectory);

            // get files
            try
            {
                // get file contents
                FileContents fileContents = fileManager.GetFile(param.FileManagerFilePaths[0]);

                // upload file along with its details and returns the new file
                ILongRunningTaskResult fileRes = this.UploadWeightMatrix(param, fileContents, context);

                return fileRes;
            }
            catch (FileContentsNotFoundException)
            {
                List<string> arg = new List<string>();
                arg.Add(param.FileManagerFilePaths[0]);
                context.TaskStatus.AddError(new LongRunningTaskError
                {
                    ErrorCode = 5200,
                    Arguments = arg
                });
            }
            catch (WeigthMatrixUploadInvalidFileException ex)
            {
                List<string> arg = new List<string>();
                arg.Add(ex.Errors);
                context.TaskStatus.AddError(new LongRunningTaskError
                {
                    ErrorCode = 5201,
                    Arguments = arg
                });
            }
            catch (WeigthMatrixUploadExcelFormatException ex)
            {
                List<string> arg = new List<string>();
                arg.Add(ex.Errors);
                context.TaskStatus.AddError(new LongRunningTaskError
                {
                    ErrorCode = 5203,
                    Arguments = arg
                });
            }
            return null;
        }

        private ILongRunningTaskResult UploadWeightMatrix(
            WeightMatrixUploadDto param,
            FileContents fileContents,
            LongRunningTaskContext context)
        {
            // create HTTP OAuth client
            OAuthClient oAuthClient = new OAuthClient(
                HostConfig.GetHostUrl() + HostConfig.GetSaveWeightMatrixResource() + "/token",
                HostConfig.GetHostUrl() + HostConfig.GetSaveWeightMatrixResource(),
                null,
                null);

            // set OAuth authentication ticket
            oAuthClient.SetAuthenticationTicket(context.Ticket);

            // create request data
            MultipartFormDataContent multipartContent = new MultipartFormDataContent();

            // add param to multipart content
            var paramContent = new ObjectContent<WeightMatrixUploadDto>(param, new JsonMediaTypeFormatter());
            multipartContent.Add(paramContent, "uploadParam");

            using (var fileStream = fileContents.StreamAccessor())
            {
                // add file contents to multipart content
                var fileHttpContent = new StreamContent(fileStream);
                fileHttpContent.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachement")
                {
                    FileName = RemoveIllegalCharacters.Remove(fileContents.Metadata.FileName)
                };
                fileHttpContent.Headers.ContentType = new MediaTypeHeaderValue(fileContents.Metadata.MimeType);
                multipartContent.Add(fileHttpContent, "uploadFile", RemoveIllegalCharacters.Remove(fileContents.Metadata.FileName));

                // POST
                HttpResponseMessage resp = oAuthClient.PostAsync(null, multipartContent, context.CancellationToken).Result;

                resp.EnsureSuccessStatusCode();

                // portfolio upload temporary folder
                var temporaryFolder = ConfigurationManager.AppSettings["ESGRatingWeightMatrixUploadTempFolder"];

                if (string.IsNullOrEmpty(temporaryFolder))
                {
                    throw new NullReferenceException("Weight matrices temporary upload folder not defined in appSettings :: ESGRatingWeightMatrixUploadTempFolder.");
                }

                // ensure that the temporary folder exists
                try
                {
                    if (Directory.Exists(temporaryFolder) == false)
                    {
                        Directory.CreateDirectory(temporaryFolder);
                    }
                }
                catch (Exception ex)
                {
                    throw new Exception(string.Format("Weight matrices temporary upload folder does not exist and couldn't be created. Path: {0}", temporaryFolder), ex);
                }

                // create multipart provider for obtaining content associated to multipart response content
                var multipartProvider = new MultipartFormDataStreamProvider(temporaryFolder);

                //retrieve data from multipart response
                resp.Content.ReadAsMultipartAsync(multipartProvider).Wait();

                // read weight matrix file
                MultipartFileData weightMatrixMultipartFile = multipartProvider.FileData.FirstOrDefault();

                if (weightMatrixMultipartFile == null)
                {
                    throw new Exception("No multipart weight matrix file data.");
                }

                // get weight matrix upload errors from FormData
                string errorsParam = multipartProvider.FormData["errorsParam"];
                string errors = JsonConvert.DeserializeObject<string>(errorsParam);

                if (errors != string.Empty)
                    throw new WeigthMatrixUploadExcelFormatException(errors);

                string filePath = weightMatrixMultipartFile.LocalFileName;
                string fileName = RemoveIllegalCharacters.Remove(weightMatrixMultipartFile.Headers.ContentDisposition.FileName);
                string mimeType = weightMatrixMultipartFile.Headers.ContentType.MediaType;

                byte[] resFileBytes = File.ReadAllBytes(filePath);

                var inMemFileRes = new LongRunningTaskFileInMemResult
                {
                    Contents = resFileBytes,
                    FileName = fileName,
                    MimeType = mimeType
                };

                return inMemFileRes;
            }
        }

        private WeightMatrixUploadDto GetParameter(string paramJson)
        {
            // check parameter
            if (string.IsNullOrEmpty(paramJson))
            {
                throw new WeigthMatrixUploadParamException();
            }

            WeightMatrixUploadDto param = null;

            // check parameter
            try
            {
                param = JsonConvert.DeserializeObject<WeightMatrixUploadDto>(paramJson);
            }
            catch (Exception ex)
            {
                throw new WeigthMatrixUploadParamException(null, ex);
            }

            // check parameter
            if (param.FileManagerFilePaths == null || param.FileManagerFilePaths.Length == 0)
            {
                throw new WeigthMatrixUploadParamException();
            }
            return param;
        }
    }
}
